#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Web应用业务服务
专门为Web界面提供数据和业务逻辑处理
"""

import logging
import time
from datetime import datetime, date
from typing import Dict, List, Any, Optional
from repositories.database_factory import database_factory
from services.historical_data_service import HistoricalDataService
from services.east_money_service import EastMoneyAPIService
from utils.trade_calendar import TradeCalendar
from config.config import TRADE_TIME


class WebAppService:
    """Web应用业务服务类"""
    
    def __init__(self):
        self.logger = logging.getLogger(__name__)
        self.mnjt_repo = database_factory.get_mnjt_repository()
        self.historical_service = HistoricalDataService()
        self.east_money_service = EastMoneyAPIService()
        self.trade_calendar = TradeCalendar()
    
    def get_today_recommendations(self, target_date: Optional[date] = None) -> Dict[str, Any]:
        """获取今日推荐的股票数据（从mnjt表按买入日期查询）"""
        try:
            start_time = time.time()
            if target_date is None:
                target_date = date.today()
            
            # 从mnjt表获取指定日期买入的所有股票（已包含新推荐和持续监听的股票）
            db_start = time.time()
            all_stocks = self.mnjt_repo.get_stocks_by_buy_date_sorted_by_score(target_date)
            db_time = time.time() - db_start
            self.logger.info(f"⏱️  数据库查询耗时: {db_time*1000:.2f}ms")
            self.logger.info(f"获取到 {len(all_stocks)} 只当日买入的股票")
            
            if not all_stocks:
                return {
                    "date": target_date.strftime('%Y-%m-%d'),
                    "top_30": [],
                    "bottom_30": [],
                    "continuous_monitoring": [],
                    "update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                }
            
            # 数据已按Score DESC排序，Score>0在前，Score=0在后
            # 找到Score=0的分界点
            zero_score_index = next((i for i, s in enumerate(all_stocks) if s.get('Score', 0) == 0), len(all_stocks))
            
            new_recommendation_stocks = all_stocks[:zero_score_index]  # Score > 0
            continuous_monitoring_stocks = all_stocks[zero_score_index:]  # Score = 0
            
            # 根据新推荐股票总数智能分配前N和后N
            total_count = len(new_recommendation_stocks)
            if total_count <= 30:
                # 股票总数不足30个时，前一半作为top，后一半作为bottom
                mid_point = total_count // 2
                top_30 = new_recommendation_stocks[:mid_point] if mid_point > 0 else new_recommendation_stocks
                bottom_30 = new_recommendation_stocks[mid_point:] if mid_point > 0 and mid_point < total_count else []
            else:
                # 股票总数超过30个时，取前30和后30
                top_30 = new_recommendation_stocks[:30]
                bottom_30 = new_recommendation_stocks[-30:]
            
            # 获取股票代码列表（包含新推荐的top/bottom和持续关注的股票）
            codes_start = time.time()
            all_codes = []
            for stock in top_30 + bottom_30 + continuous_monitoring_stocks:
                all_codes.append(stock.get('code', ''))
            codes_time = time.time() - codes_start
            self.logger.info(f"⏱️  股票代码处理耗时: {codes_time*1000:.2f}ms")
            
            # 智能实时数据获取：根据交易时间判断是否需要获取实时数据
            api_start = time.time()
            real_time_data = self._get_smart_stock_data(all_codes)
            api_time = time.time() - api_start
            self.logger.info(f"⏱️  股票数据获取耗时: {api_time*1000:.2f}ms")
            
            # 获取昨日成交量（前一个交易日）
            volume_start = time.time()
            yesterday = self.trade_calendar.get_previous_trade_day(target_date)
            prev_volumes = self._get_previous_volumes(all_codes, yesterday, target_date)
            volume_time = time.time() - volume_start
            self.logger.info(f"⏱️  昨日成交量获取耗时: {volume_time*1000:.2f}ms")

            self.logger.debug(f"prev_volumes数量: {len(prev_volumes)}")
            
            # 格式化数据
            format_start = time.time()
            formatted_top_30 = []
            for stock in top_30:
                formatted_stock = self._format_recommendation_stock(stock, real_time_data, prev_volumes)
                formatted_top_30.append(formatted_stock)
            
            formatted_bottom_30 = []
            for stock in bottom_30:
                formatted_stock = self._format_recommendation_stock(stock, real_time_data, prev_volumes)
                formatted_bottom_30.append(formatted_stock)
            
            # 格式化持续监听股票
            formatted_continuous_monitoring = []
            for stock in continuous_monitoring_stocks:
                formatted_stock = self._format_recommendation_stock(stock, real_time_data, prev_volumes)
                formatted_continuous_monitoring.append(formatted_stock)
                
            format_time = time.time() - format_start
            self.logger.info(f"⏱️  数据格式化耗时: {format_time*1000:.2f}ms")
            
            total_time = time.time() - start_time
            self.logger.info(f"⏱️  总耗时: {total_time*1000:.2f}ms")
            
            return {
                "date": target_date.strftime('%Y-%m-%d'),
                "top_30": formatted_top_30,
                "bottom_30": formatted_bottom_30,
                "continuous_monitoring": formatted_continuous_monitoring,
                "statistics": {
                    "new_recommendations": len(new_recommendation_stocks),
                    "continuous_monitoring": len(continuous_monitoring_stocks),
                    "total_stocks": len(all_stocks)
                },
                "update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            }
            
        except Exception as e:
            self.logger.error(f"获取推荐数据失败: {e}")
            raise
    
    def check_system_health(self) -> Dict[str, Any]:
        """系统健康检查"""
        try:
            # 检查数据库连接
            database_factory.validate_all_connections()
            
            # 检查数据完整性
            db_info = database_factory.get_database_info()
            
            return {
                "status": "healthy",
                "database": "connected",
                "zbfx_records": db_info.get("zbfx_table", {}).get("total_rows", 0),
                "mnjt_records": db_info.get("mnjt_table", {}).get("total_rows", 0),
                "timestamp": datetime.now().isoformat()
            }
        except Exception as e:
            self.logger.error(f"健康检查失败: {e}")
            return {
                "status": "unhealthy",
                "error": str(e),
                "timestamp": datetime.now().isoformat()
            }
    
    # ==================== 私有方法：数据获取和格式化 ====================
    
    def _get_previous_volumes(self, stock_codes: List[str], yesterday: date, target_date: date = None) -> Dict[str, int]:
        """获取昨日成交量（智能优化版：区分昨日存在和不存在的股票）"""
        try:
            # 1. 优先从mnjt表的chengjiaoliang字段获取（>0的数据）
            db_volume_start = time.time()
            prev_volumes = self.mnjt_repo.get_previous_volumes_from_chengjiaoliang(stock_codes, yesterday)
            db_volume_time = time.time() - db_volume_start
            self.logger.info(f"⏱️    数据库成交量查询耗时: {db_volume_time*1000:.2f}ms")
            
            # 2. 查询昨日在mnjt表中存在的所有股票（区分"存在但成交量为0"和"完全不存在"）
            db_exists_start = time.time()
            yesterday_existing_codes = self.mnjt_repo.get_existing_codes_by_date(stock_codes, yesterday)
            db_exists_time = time.time() - db_exists_start
            self.logger.info(f"⏱️    昨日存在性查询耗时: {db_exists_time*1000:.2f}ms")
            
            # 3. 分类处理missing的股票
            missing_codes = [code for code in stock_codes if code not in prev_volumes]
            
            if missing_codes:
                # 区分两类missing股票
                yesterday_existed_but_zero = [code for code in missing_codes if code in yesterday_existing_codes]
                never_existed_yesterday = [code for code in missing_codes if code not in yesterday_existing_codes]
                
                self.logger.debug(f"昨日存在但成交量为0: {len(yesterday_existed_but_zero)} 只")
                self.logger.debug(f"昨日完全不存在: {len(never_existed_yesterday)} 只")
                
                # 4. 对昨日存在但成交量为0的股票查询历史API
                if yesterday_existed_but_zero:
                    hist_start = time.time()
                    missing_volumes = self.historical_service.get_batch_previous_day_volumes(yesterday_existed_but_zero, yesterday)
                    hist_time = time.time() - hist_start
                    self.logger.info(f"⏱️    历史数据服务耗时: {hist_time*1000:.2f}ms (查询 {len(yesterday_existed_but_zero)} 只)")
                    
                    # 更新有效成交量
                    valid_volumes = {}
                    for code, volume in missing_volumes.items():
                        if volume is not None and volume > 0:
                            prev_volumes[code] = volume
                            valid_volumes[code] = volume
                    
                    # 批量更新数据库
                    if valid_volumes:
                        try:
                            update_start = time.time()
                            updated_count = self.mnjt_repo.batch_update_chengjiaoliang(valid_volumes, yesterday)
                            update_time = time.time() - update_start
                            self.logger.info(f"⏱️    批量更新数据库耗时: {update_time*1000:.2f}ms")
                            if updated_count > 0:
                                self.logger.info(f"批量更新 {updated_count} 条记录的chengjiaoliang字段")
                        except Exception as e:
                            self.logger.warning(f"批量更新chengjiaoliang字段失败: {e}")
                
                # 5. 对昨日完全不存在的股票，获取真实的昨日成交量并缓存
                if never_existed_yesterday:
                    cache_start = time.time()
                    cached_volumes = self._get_cached_volumes_for_new_stocks(never_existed_yesterday, yesterday)
                    cache_time = time.time() - cache_start
                    self.logger.info(f"⏱️    缓存成交量查询耗时: {cache_time*1000:.2f}ms (查询 {len(never_existed_yesterday)} 只)")
                    
                    for code, volume in cached_volumes.items():
                        if volume is not None and volume > 0:
                            prev_volumes[code] = volume
                            self.logger.debug(f"新股票 {code} 昨日成交量: {volume}")
            
            return prev_volumes
        except Exception as e:
            self.logger.error(f"获取昨日成交量失败: {e}")
            return {}
    
    def _format_recommendation_stock(self, stock: Dict[str, Any], real_time_data: Dict[str, Any], prev_volumes: Dict[str, int]) -> Dict[str, Any]:
        """格式化推荐股票数据"""
        code = stock.get('code', '')
        
        # 获取实时数据（从StockInfo对象）
        stock_info = real_time_data.get(code)
        if stock_info:
            name = stock_info.name or ''
            change_percent = stock_info.change_percent or 0
            volume = stock_info.volume or 0
        else:
            name = ''
            change_percent = 0
            volume = 0
        
        if code == "603717":
            self.logger.info(f"【DEBUG 603717】格式化推荐股票数据: {stock}")
            self.logger.info(f"【DEBUG 603717】实时数据: {real_time_data[code]}")
            self.logger.info(f"【DEBUG 603717】昨日成交量: {prev_volumes.get(code, 0)}")
        
        # 获取昨日成交量
        prev_volume = prev_volumes.get(code, 0)
        
        # 计算能量值
        energy_value = None
        if prev_volume and prev_volume > 0 and volume > 0:
            volume_ratio = volume / prev_volume
            if volume_ratio > 0:
                energy_value = change_percent / volume_ratio
        
        # 直接使用数据库中的days字段作为持仓天数
        holding_days = stock.get('days', 0)
        
        return {
            "code": code,
            "name": name,
            "change_percent": change_percent,
            "volume": volume,
            "prev_volume": prev_volume,
            "energy_value": round(energy_value, 4) if energy_value is not None else None,
            "holding_days": holding_days
        }
    
    def _get_smart_stock_data(self, stock_codes: List[str]) -> Dict[str, Any]:
        """
        智能获取股票数据：根据交易时间判断是否需要获取实时数据
        
        Args:
            stock_codes: 股票代码列表
        
        Returns:
            股票数据字典（StockInfo对象）
        """
        current_time = datetime.now().time()
        sell_time_obj = datetime.strptime(TRADE_TIME['sell_time'], '%H:%M').time()
        
        # 判断是否是交易日（只在交易日才做时间判断）
        today = date.today()
        is_trade_day = self.trade_calendar.is_trade_day(today)
        
        # 非交易日或收盘后：从mnjt表获取已有交易数据
        if not is_trade_day or current_time >= sell_time_obj:
            if not is_trade_day:
                self.logger.info(f"📅 非交易日({today})，从mnjt表获取已有交易数据")
            else:
                self.logger.info(f"🕰️ 股市已收盘({current_time} >= {sell_time_obj})，从mnjt表获取已有交易数据")
            
            return self._get_stock_data_from_mnjt(stock_codes)
        
        # 交易时间内：正常获取实时数据
        self.logger.info(f"📊 交易时间内({current_time} < {sell_time_obj})，获取实时数据")
        return self.east_money_service.get_stocks_by_codes(stock_codes)
    
    def _get_stock_data_from_mnjt(self, stock_codes: List[str]) -> Dict[str, Any]:
        """
        从mnjt表获取股票交易数据（收盘后使用）
        
        Args:
            stock_codes: 股票代码列表
        
        Returns:
            股票数据字典（StockInfo格式）
        """
        try:
            stock_data = {}
            
            # 批量查询mnjt表中的股票数据（优化版本）
            stock_data = self._batch_get_mnjt_stock_data(stock_codes)
            
            self.logger.info(f"从mnjt表成功获取 {len(stock_data)}/{len(stock_codes)} 只股票的数据")
            return stock_data
            
        except Exception as e:
            self.logger.error(f"从mnjt表获取股票数据失败: {e}")
            return self._create_empty_stock_data(stock_codes)
    
    def _batch_get_mnjt_stock_data(self, stock_codes: List[str]) -> Dict[str, Any]:
        """
        批量从mnjt表获取股票数据（优化性能）
        
        Args:
            stock_codes: 股票代码列表
        
        Returns:
            股票数据字典
        """
        from models.stock_model import StockInfo
        stock_data = {}
        
        if not stock_codes:
            return stock_data
        
        try:
            # 使用子查询代替CTE（兼容性更好）
            placeholders = ','.join(['?' for _ in stock_codes])
            query = f"""
            SELECT t1.code, t1.jiage, t1.zhangfu, t1.chengjiaoliang
            FROM [GP].[dbo].[mnjt] t1
            WHERE t1.code IN ({placeholders})
            AND t1.id = (
                SELECT MAX(t2.id) 
                FROM [GP].[dbo].[mnjt] t2 
                WHERE t2.code = t1.code
            )
            """
            
            results = self.mnjt_repo.execute_query(query, stock_codes)
            
            # 使用带缓存的方法获取所有股票的名称
            stock_names = self.east_money_service.get_stock_names(stock_codes)
            
            # 创建结果映射
            found_codes = set()
            for row in results:
                code = row.get('code')
                if code:
                    found_codes.add(code)
                    stock_info = StockInfo(
                        code=code,
                        name=stock_names.get(code, ''),  # 从获取的名称字典中查找
                        current_price=row.get('jiage', 0) or 0,
                        change_percent=row.get('zhangfu', 0) or 0,
                        volume=row.get('chengjiaoliang', 0) or 0
                    )
                    stock_data[code] = stock_info
                    self.logger.debug(f"从mnjt获取 {code}: price={stock_info.current_price}, change={stock_info.change_percent}%, volume={stock_info.volume}")
            
            # 为没有找到记录的股票创建空数据
            for code in stock_codes:
                if code not in found_codes:
                    stock_info = StockInfo(
                        code=code,
                        name=stock_names.get(code, ''),  # 同样为未找到的股票添加名称
                        current_price=0,
                        change_percent=0,
                        volume=0
                    )
                    stock_data[code] = stock_info
                    self.logger.debug(f"mnjt表中未找到 {code} 的记录，使用空数据")
            
            self.logger.debug(f"批量查询mnjt表: {len(found_codes)}/{len(stock_codes)} 只股票有数据")
            return stock_data
            
        except Exception as e:
            self.logger.error(f"批量查询mnjt表失败: {e}")
            
            # 异常时返回空数据
            for code in stock_codes:
                stock_info = StockInfo(
                    code=code,
                    name='',
                    current_price=0,
                    change_percent=0,
                    volume=0
                )
                stock_data[code] = stock_info
            
            return stock_data
            
        except Exception as e:
            self.logger.error(f"从mnjt表获取股票数据失败: {e}")
            return self._create_empty_stock_data(stock_codes)
    
    def _get_cached_volumes_for_new_stocks(self, stock_codes: List[str], target_date: date) -> Dict[str, int]:
        """
        为昨日不存在的新股票获取真实的昨日成交量（使用Redis缓存优化）
        
        Args:
            stock_codes: 股票代码列表（昨日完全不存在的股票）
            target_date: 目标日期（昨日）
        
        Returns:
            股票代码到昨日成交量的映射
        """
        from utils.redis_manager import redis_manager
        
        result_volumes = {}
        
        if not stock_codes:
            return result_volumes
            
        try:
            # 1. 先从Redis缓存查询
            cached_volumes = redis_manager.get_batch_stock_volumes(stock_codes, target_date)
            
            # 分离已缓存和未缓存的股票
            cached_codes = []
            need_api_codes = []
            
            for code in stock_codes:
                cached_volume = cached_volumes.get(code)
                if cached_volume is not None:
                    result_volumes[code] = cached_volume
                    cached_codes.append(code)
                    self.logger.debug(f"从缓存获取 {code} 昨日成交量: {cached_volume}")
                else:
                    need_api_codes.append(code)
            
            self.logger.info(f"缓存命中: {len(cached_codes)}/{len(stock_codes)} 只股票")
            
            # 2. 对未缓存的股票调用历史API
            if need_api_codes:
                self.logger.info(f"调用历史API获取 {len(need_api_codes)} 只股票的昨日成交量")
                api_volumes = self.historical_service.get_batch_previous_day_volumes(need_api_codes, target_date)
                
                # 合并结果并缓存新数据
                cache_data = {}
                for code, volume in api_volumes.items():
                    result_volumes[code] = volume
                    if volume is not None and volume > 0:
                        cache_data[code] = volume
                
                # 3. 将新获取的数据缓存到Redis
                if cache_data:
                    cached_count = redis_manager.set_batch_stock_volumes(cache_data, target_date)
                    self.logger.info(f"缓存新数据: {cached_count} 只股票的昨日成交量")
            
            return result_volumes
            
        except Exception as e:
            self.logger.error(f"获取缓存成交量失败: {e}")
            # 异常时回退到直接API调用
            return self.historical_service.get_batch_previous_day_volumes(stock_codes, target_date)
    
    def _create_empty_stock_data(self, stock_codes: List[str]) -> Dict[str, Any]:
        """创建空的股票数据结构"""
        from models.stock_model import StockInfo
        stock_data = {}
        for code in stock_codes:
            stock_info = StockInfo(
                code=code,
                name='',
                current_price=0,
                change_percent=0,
                volume=0
            )
            stock_data[code] = stock_info
        return stock_data